Authors
- Francesco Sgrelli
- Álvaro Mínguez Bellón
EXERCISE:
Apply PDP to the regression example of predicting bike rentals. Fit a random forest approximation for the prediction of bike rentals (cnt). Use the partial dependence plot to visualize the relationships the model learned. Use the slides shown in class as model.
QUESTION:
Analyse the influence of days since 2011, temperature, humidity and wind speed on the predicted bike counts.
library(dplyr)
package 㤼㸱dplyr㤼㸲 was built under R version 4.0.5
Attaching package: 㤼㸱dplyr㤼㸲
The following objects are masked from 㤼㸱package:stats㤼㸲:
filter, lag
The following objects are masked from 㤼㸱package:base㤼㸲:
intersect, setdiff, setequal, union
library(plotly)
package 㤼㸱plotly㤼㸲 was built under R version 4.0.5Loading required package: ggplot2
package 㤼㸱ggplot2㤼㸲 was built under R version 4.0.5Registered S3 method overwritten by 'data.table':
method from
print.data.table
Attaching package: 㤼㸱plotly㤼㸲
The following object is masked from 㤼㸱package:ggplot2㤼㸲:
last_plot
The following object is masked from 㤼㸱package:stats㤼㸲:
filter
The following object is masked from 㤼㸱package:graphics㤼㸲:
layout
library(reshape2)
package 㤼㸱reshape2㤼㸲 was built under R version 4.0.5
library(lubridate)
package 㤼㸱lubridate㤼㸲 was built under R version 4.0.5
Attaching package: 㤼㸱lubridate㤼㸲
The following objects are masked from 㤼㸱package:base㤼㸲:
date, intersect, setdiff, union
library(randomForestSRC)
package 㤼㸱randomForestSRC㤼㸲 was built under R version 4.0.5
randomForestSRC 3.1.0
Type rfsrc.news() to see new features, changes, and bug fixes.
#setwd("/Users/cmonserr/OneDrive - UPV/Trabajo_2/Asignaturas/Evaluacion de modelos/Practicas/Practica 3/Bike-Sharing-Dataset")
days <- read.csv("day.csv")
hour <- read.csv("hour.csv")
days$dteday <- as_date(days$dteday)
days_since <- select(days, workingday, holiday, temp, hum, windspeed, cnt)
days_since$days_since_2011 <- int_length(interval(ymd("2011-01-01"), days$dteday)) / (3600*24)
days_since$SUMMER <- ifelse(days$season == 3, 1, 0)
days_since$FALL <- ifelse(days$season == 4, 1, 0)
days_since$WINTER <- ifelse(days$season == 1, 1, 0)
days_since$MISTY <- ifelse(days$weathersit == 2, 1, 0)
days_since$RAIN <- ifelse(days$weathersit == 3 | days$weathersit == 4, 1, 0)
days_since$temp <- days_since$temp * 47 - 8
days_since$hum <- days_since$hum * 100
days_since$windspeed <- days_since$windspeed * 67
rf <- rfsrc(cnt~., data=days_since)
results <- select(days_since, days_since_2011, temp, hum, windspeed, cnt)
nr <- nrow(days_since)
for(c in names(results)[1:4])
{
for(i in 1:nr){
r <- days_since
r[[c]] <- days_since[[c]][i]
sal <- predict(rf, r)$predicted
results[[c]][i] <- sum(sal) / nr
}
}
g1 <- ggplot(days_since, aes(x=days_since_2011, y=results$days_since_2011)) + geom_line()+ ylim(c(0,6000)) + geom_rug(alpha=0.1, sides="b") + ylab("Prediction") + xlab("Days since 2011")
g2 <- ggplot(days_since, aes(x=temp, y=results$temp)) + geom_line() + ylim(c(0,6000)) + geom_rug(alpha=0.1, sides="b") + xlab("Temperature")
g3 <- ggplot(days_since, aes(x=hum, y=results$hum)) + geom_line() + ylim(c(0,6000)) + geom_rug(alpha=0.1, sides="b") + xlab("Humidity")
g4 <- ggplot(days_since, aes(x=windspeed, y=results$windspeed)) + geom_line() + ylim(c(0,6000)) + geom_rug(alpha=0.1, sides="b")+ xlab("Wind speed")
subplot(g1, g2, g3, g4, shareX = FALSE, shareY = TRUE, titleX = TRUE)
NA
Interpretación
Days since 2011: A medida que pasan más días desde 2011, en general, se tienden a vender cada vez más bicicletas. No obstante, hay que mencionar dos aspectos importantes. El primero es que entre los 130 y los 350 días, la diferencia de la importancia es mínima en esta variable (siempre en torno a 3700-3800 bicicletas más). El segundo es que la tendencia es siempre creciente a excepción del tramo final, pues a partir de los 662 días se pasa de vender 5814 bicicletas más a vender 4799, es decir que la influencia del paso del tiempo deja de ser siempre creciente. Estas explicaciones son fiables, pues se tienen observaciones para todos los valores de esta variable.
Temperature: Lo más importante en esta variable es que, empezando por el valor más bajo (5 grados bajo 0), según la temperatura aumenta, se venden más bicicletas (pasando de vender 3094 bicicletas con -5 grados hasta 5126 bicicletas con 16 grados). No obstante, esta tendencia creciente se frena en los 16 grados, para mantenerse estable hasta los 26 grados aproximadamente. A partir de esa temperatura, por cada grado que aumenta la temperatura se venderán menos bicicletas. En otras palabras, se venderán más bicicletas cuando la temperatura sea agradable (16-26 grados). Cuando hace frío o calor, se venden cada vez menos bicicletas. Finalmente, hay que tener cuidado con las explicaciones cuando las temperaturas son menores que 0.5 grados o mayores que 29, pues se tienen menos observaciones cuando la temperatura toma esos valores.
Humidity: Mientras la humedad es menor que 50 el número de bicicletas vendidas se mantendrá siempre constante en 4700 bicicletas aproximadamente. Una vez superado ese umbral del 50% de humedad, según aumenta la humedad, se venden cada vez menos bicicletas, hasta llegar a las 3490 bicicletas vendidas cuando la humedad es del 97%. No obstante, estas explicaciones deben ser revisadas cuando la humedad se encuentra por debajo del 37% o por encima del 92%, pues en esos casos no se tienen muchas observaciones, por lo que las explicaciones en esos casos no son del todo fiables.
Wind speed: El modelo predice que la tendencia de esta variable es claramente decreciente. Según aumenta el viento cada vez se venden menos bicicletas, pasando de 4640 cuando el viento toma un valor de 1.5, a 3992 cuando el viento toma un valor de 24. Para valores mayores que 24, el número de bicicletas vendidas se mantendrá estable en ese valor. No obstante, para valores mayores que 24, hay que reconsiderar la fiabilidad de estas explicaciones, pues se tienen muy pocas (o ninguna) observaciones para esos valores en esta variable.
EXERCISE:
Generate a 2D Partial Dependency Plot with humidity and temperature to predict the number of bikes rented depending of those parameters.
BE CAREFUL: due to the size, extract a set of random samples from the BBDD before generating the the data for the Partial Dependency Plot.
Show the density distribution of both input features with the 2D plot as shown in the class slides.
TIP: Use geom_tile() to generate the 2D plot. Set width and height to avoid holes.
QUESTION:
Interpret the results.
sampled <- sample_n(days_since, 40)
temp <- sampled$temp
hum <- sampled$hum
th <- inner_join(data.frame(temp),data.frame(hum), by=character())
th$p <- 0
for(i in 1:nrow(th)){
r <- days_since
r[["temp"]] <- th[["temp"]][i]
r[["hum"]] <- th[["hum"]][i]
sal <- predict(rf, r)$predicted
th[["p"]][i] <- sum(sal) / nr
}
ggplot(th, aes(x=temp, y=hum)) + geom_tile(aes(fill=p, width=10, height=15)) + geom_rug(alpha=0.01)

Interpretacion
Podemos apreciar una cierta independencia entre el efecto de la temperatura y el de la humedad, ya que, como se ha podido observar en el PDP en 1 dimensión de ambas variables, mientras la humedad se mantenga por debajo del 50% el número de bicicletas vendidas será máximo, y a partir de ese umbral irá disminuyendo el número de ventas según aumente la humedad. Por otro lado, en el caso de la temperatura, partiendo desde la temperatura mínima, según aumenta la temperatura aumenta el número de ventas, hasta llegar a un máximo sobre los 16 grados, donde el número de bicicletas vendidas se mantiene constante hasta llegar a los 26 grados. A partir de ahí vuelve a disminuir el número de bicicletas vendidas según aumenta la temperatura.
En el PDP en 2D podemos apreciar estos mismos fenómenos de manera idéntica. Vemos que el número máximo de ventas se alcanzará cuando la temperatura sea agradable (en torno a 20 grados) y la humedad sea menor que el 50%; mientras que este número alcanzará su mínimo cuando la temperatura sea extremadamente baja y la humedad muy alta.
Asumimos que los efectos de ambas variables son independientes porque estos mismos efectos se pueden apreciar por separado, sin hacer uso del PDP en 2 dimensiones, y por tanto no es de interés estudiar su interacción.
También hay que tener en consideración que no se tienen muchas observaciones reales de todos los valores que se han probado, con lo cual el modelo no se ha podido entrenar en esos escenarios y las explicaciones pueden no ser fiables. Es el caso de la humedad cuando toma valores por debajo del 37% o por encima del 92%; o de la temperatura cuando está por debajo de 0.5 o por encima de 29 grados. Además, cabe mencionar que no se tienen datos tampoco de cuántas observaciones del conjunto se tienen para cada posible caso de interacción de ambas variables.
EXERCISE:
Apply the previous concepts to predict the price of a house from the database kc_house_data.csv. In this case, use again a random forest approximation for the prediction based on the features bedrooms, bathrooms, sqft_living, sqft_lot, floors and yr_built. Use the partial dependence plot to visualize the relationships the model learned.
BE CAREFUL: due to the size, extract a set of random samples from the BBDD before generating the data for the Partial Dependency Plot.
QUESTION:
Analyse the influence of bedrooms, bathrooms, sqft_living and floors on the predicted price.
d <- read.csv("kc_house_data.csv")
set.seed(50)
sampled <- sample_n(d, 1000)
sampled <- select(sampled, bedrooms, bathrooms, sqft_living, sqft_lot, floors, yr_built, price)
rf <- rfsrc(price~., data=sampled)
results <- select(sampled, bedrooms, bathrooms, sqft_living, floors, price)
nr <- nrow(sampled)
for(c in names(results)[1:4])
{
for(i in 1:nr){
r <- sampled
r[[c]] <- sampled[[c]][i]
sal <- predict(rf, r)$predicted
results[[c]][i] <- sum(sal) / nr
}
}
g1 <- ggplot(sampled, aes(x=bedrooms, y=results$bedrooms)) + geom_line() + geom_rug(alpha=0.1, sides="b") + ylab("Prediction") + xlab("Bedrooms")
g2 <- ggplot(sampled, aes(x=bathrooms, y=results$bathrooms)) + geom_line() + geom_rug(alpha=0.1, sides="b") + xlab("Bathrooms")
g3 <- ggplot(sampled, aes(x=sqft_living, y=results$sqft_living)) + geom_line() + geom_rug(alpha=0.1, sides="b") + xlab("Sqft Living")
g4 <- ggplot(sampled, aes(x=floors, y=results$floors)) + geom_line() + geom_rug(alpha=0.1, sides="b")+ xlab("Floors")
subplot(g1, g2, g3, g4, titleX = TRUE, shareX = FALSE)
Interpretacion
bedrooms: Esta variable se comporta de una manera curiosa, pues, partiendo de 0 habitaciones, a más habitaciones, más barata será la vivienda, hasta llegar a 4 habitaciones. A partir de ese punto se invierte la tendencia, y a mayor número de habitaciones, mayor precio. No obstante, no se puede afirmar con firmeza esto, ya que se tienen pocas observaciones para la variable cuando se tienen 0 o más de 6 habitaciones.
bathrooms: Se puede apreciar claramente como a mayor número de cuartos de baños, mayor será el precio de la vivienda en cuestión. No obstante, esta afirmación no es del todo fiable para 0 o más de 4 baños, pues apenas se tienen muestras de entrenamiento con estos valores.
sqft_living: Podemos apreciar como claramente, cuánto mayor sea la superficie del salón, más cara será la vivienda en cuestión. No obstante, esta afirmación está en duda para valores menores que 560 pies cuadrados y mayores que 4900 pies cuadrados.
floors: En esta variable podemos afirmar que a mayor número de pisos, mayor precio tendrá la vivienda, y es una explicación fiable, pues se tienen observaciones para todos los valores en el set de entrenamiento.
LS0tDQp0aXRsZTogIlhBSSAzOiBNb2RlbC1BZ25vc3RpYyBtZXRob2RzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KKipBdXRob3JzKioNCg0KLSBGcmFuY2VzY28gU2dyZWxsaQ0KLSDDgWx2YXJvIE3DrW5ndWV6IEJlbGzDs24NCg0KIyMgRVhFUkNJU0U6DQoNCkFwcGx5IFBEUCB0byB0aGUgcmVncmVzc2lvbiBleGFtcGxlIG9mIHByZWRpY3RpbmcgYmlrZSByZW50YWxzLiBGaXQgYSByYW5kb20gZm9yZXN0IGFwcHJveGltYXRpb24gZm9yIHRoZSBwcmVkaWN0aW9uIG9mIGJpa2UgcmVudGFscyAoKipjbnQqKikuIFVzZSB0aGUgcGFydGlhbCBkZXBlbmRlbmNlIHBsb3QgdG8gdmlzdWFsaXplIHRoZSByZWxhdGlvbnNoaXBzIHRoZSBtb2RlbCBsZWFybmVkLiBVc2UgdGhlIHNsaWRlcyBzaG93biBpbiBjbGFzcyBhcyBtb2RlbC4gIA0KDQojIyBRVUVTVElPTjoNCg0KQW5hbHlzZSB0aGUgaW5mbHVlbmNlIG9mICoqZGF5cyBzaW5jZSAyMDExLCB0ZW1wZXJhdHVyZSwgaHVtaWRpdHkqKiBhbmQgKip3aW5kIHNwZWVkKiogb24gdGhlIHByZWRpY3RlZCBiaWtlIGNvdW50cy4NCg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkocmFuZG9tRm9yZXN0U1JDKQ0KDQojc2V0d2QoIi9Vc2Vycy9jbW9uc2Vyci9PbmVEcml2ZSAtIFVQVi9UcmFiYWpvXzIvQXNpZ25hdHVyYXMvRXZhbHVhY2lvbiBkZSBtb2RlbG9zL1ByYWN0aWNhcy9QcmFjdGljYSAzL0Jpa2UtU2hhcmluZy1EYXRhc2V0IikNCmRheXMgPC0gcmVhZC5jc3YoImRheS5jc3YiKQ0KaG91ciA8LSByZWFkLmNzdigiaG91ci5jc3YiKQ0KDQpkYXlzJGR0ZWRheSA8LSBhc19kYXRlKGRheXMkZHRlZGF5KQ0KZGF5c19zaW5jZSA8LSBzZWxlY3QoZGF5cywgd29ya2luZ2RheSwgaG9saWRheSwgdGVtcCwgaHVtLCB3aW5kc3BlZWQsIGNudCkNCmRheXNfc2luY2UkZGF5c19zaW5jZV8yMDExIDwtIGludF9sZW5ndGgoaW50ZXJ2YWwoeW1kKCIyMDExLTAxLTAxIiksIGRheXMkZHRlZGF5KSkgLyAoMzYwMCoyNCkNCmRheXNfc2luY2UkU1VNTUVSIDwtIGlmZWxzZShkYXlzJHNlYXNvbiA9PSAzLCAxLCAwKQ0KZGF5c19zaW5jZSRGQUxMIDwtIGlmZWxzZShkYXlzJHNlYXNvbiA9PSA0LCAxLCAwKQ0KZGF5c19zaW5jZSRXSU5URVIgPC0gaWZlbHNlKGRheXMkc2Vhc29uID09IDEsIDEsIDApDQpkYXlzX3NpbmNlJE1JU1RZIDwtIGlmZWxzZShkYXlzJHdlYXRoZXJzaXQgPT0gMiwgMSwgMCkNCmRheXNfc2luY2UkUkFJTiA8LSBpZmVsc2UoZGF5cyR3ZWF0aGVyc2l0ID09IDMgfCBkYXlzJHdlYXRoZXJzaXQgPT0gNCwgMSwgMCkNCmRheXNfc2luY2UkdGVtcCA8LSBkYXlzX3NpbmNlJHRlbXAgKiA0NyAtIDgNCmRheXNfc2luY2UkaHVtIDwtIGRheXNfc2luY2UkaHVtICogMTAwDQpkYXlzX3NpbmNlJHdpbmRzcGVlZCA8LSBkYXlzX3NpbmNlJHdpbmRzcGVlZCAqIDY3DQoNCnJmIDwtIHJmc3JjKGNudH4uLCBkYXRhPWRheXNfc2luY2UpDQoNCnJlc3VsdHMgPC0gc2VsZWN0KGRheXNfc2luY2UsIGRheXNfc2luY2VfMjAxMSwgdGVtcCwgaHVtLCB3aW5kc3BlZWQsIGNudCkNCm5yIDwtIG5yb3coZGF5c19zaW5jZSkNCmZvcihjIGluIG5hbWVzKHJlc3VsdHMpWzE6NF0pDQp7DQogIGZvcihpIGluIDE6bnIpew0KICAgIHIgPC0gZGF5c19zaW5jZQ0KICAgIHJbW2NdXSA8LSBkYXlzX3NpbmNlW1tjXV1baV0NCiAgICBzYWwgPC0gcHJlZGljdChyZiwgcikkcHJlZGljdGVkDQogICAgcmVzdWx0c1tbY11dW2ldIDwtIHN1bShzYWwpIC8gbnINCiAgfQ0KfQ0KDQpnMSA8LSBnZ3Bsb3QoZGF5c19zaW5jZSwgYWVzKHg9ZGF5c19zaW5jZV8yMDExLCB5PXJlc3VsdHMkZGF5c19zaW5jZV8yMDExKSkgKyBnZW9tX2xpbmUoKSsgeWxpbShjKDAsNjAwMCkpICsgZ2VvbV9ydWcoYWxwaGE9MC4xLCBzaWRlcz0iYiIpICsgeWxhYigiUHJlZGljdGlvbiIpICsgeGxhYigiRGF5cyBzaW5jZSAyMDExIikNCg0KZzIgPC0gZ2dwbG90KGRheXNfc2luY2UsIGFlcyh4PXRlbXAsIHk9cmVzdWx0cyR0ZW1wKSkgKyBnZW9tX2xpbmUoKSArIHlsaW0oYygwLDYwMDApKSArIGdlb21fcnVnKGFscGhhPTAuMSwgc2lkZXM9ImIiKSArIHhsYWIoIlRlbXBlcmF0dXJlIikNCg0KZzMgPC0gZ2dwbG90KGRheXNfc2luY2UsIGFlcyh4PWh1bSwgeT1yZXN1bHRzJGh1bSkpICsgZ2VvbV9saW5lKCkgKyB5bGltKGMoMCw2MDAwKSkgKyBnZW9tX3J1ZyhhbHBoYT0wLjEsIHNpZGVzPSJiIikgKyB4bGFiKCJIdW1pZGl0eSIpDQoNCmc0IDwtIGdncGxvdChkYXlzX3NpbmNlLCBhZXMoeD13aW5kc3BlZWQsIHk9cmVzdWx0cyR3aW5kc3BlZWQpKSArIGdlb21fbGluZSgpICsgeWxpbShjKDAsNjAwMCkpICsgZ2VvbV9ydWcoYWxwaGE9MC4xLCBzaWRlcz0iYiIpKyB4bGFiKCJXaW5kIHNwZWVkIikNCg0Kc3VicGxvdChnMSwgZzIsIGczLCBnNCwgc2hhcmVYID0gRkFMU0UsIHNoYXJlWSA9IFRSVUUsIHRpdGxlWCA9IFRSVUUpDQoNCmBgYA0KDQoqKkludGVycHJldGFjacOzbioqDQoNCi0gYERheXMgc2luY2UgMjAxMWA6IEEgbWVkaWRhIHF1ZSBwYXNhbiBtw6FzIGTDrWFzIGRlc2RlIDIwMTEsIGVuIGdlbmVyYWwsIHNlIHRpZW5kZW4gYSB2ZW5kZXIgY2FkYSB2ZXogbcOhcyBiaWNpY2xldGFzLiBObyBvYnN0YW50ZSwgaGF5IHF1ZSBtZW5jaW9uYXIgZG9zIGFzcGVjdG9zIGltcG9ydGFudGVzLiBFbCBwcmltZXJvIGVzIHF1ZSBlbnRyZSBsb3MgMTMwIHkgbG9zIDM1MCBkw61hcywgbGEgZGlmZXJlbmNpYSBkZSBsYSBpbXBvcnRhbmNpYSBlcyBtw61uaW1hIGVuIGVzdGEgdmFyaWFibGUgKHNpZW1wcmUgZW4gdG9ybm8gYSAzNzAwLTM4MDAgYmljaWNsZXRhcyBtw6FzKS4gRWwgc2VndW5kbyBlcyBxdWUgbGEgdGVuZGVuY2lhIGVzIHNpZW1wcmUgY3JlY2llbnRlIGEgZXhjZXBjacOzbiBkZWwgdHJhbW8gZmluYWwsIHB1ZXMgYSBwYXJ0aXIgZGUgbG9zIDY2MiBkw61hcyBzZSBwYXNhIGRlIHZlbmRlciA1ODE0IGJpY2ljbGV0YXMgbcOhcyBhIHZlbmRlciA0Nzk5LCBlcyBkZWNpciBxdWUgbGEgaW5mbHVlbmNpYSBkZWwgcGFzbyBkZWwgdGllbXBvIGRlamEgZGUgc2VyIHNpZW1wcmUgY3JlY2llbnRlLg0KRXN0YXMgZXhwbGljYWNpb25lcyBzb24gZmlhYmxlcywgcHVlcyBzZSB0aWVuZW4gb2JzZXJ2YWNpb25lcyBwYXJhIHRvZG9zIGxvcyB2YWxvcmVzIGRlIGVzdGEgdmFyaWFibGUuDQoNCi0gYFRlbXBlcmF0dXJlYDogTG8gbcOhcyBpbXBvcnRhbnRlIGVuIGVzdGEgdmFyaWFibGUgZXMgcXVlLCBlbXBlemFuZG8gcG9yIGVsIHZhbG9yIG3DoXMgYmFqbyAoNSBncmFkb3MgYmFqbyAwKSwgc2Vnw7puIGxhIHRlbXBlcmF0dXJhIGF1bWVudGEsIHNlIHZlbmRlbiBtw6FzIGJpY2ljbGV0YXMgKHBhc2FuZG8gZGUgdmVuZGVyIDMwOTQgYmljaWNsZXRhcyBjb24gLTUgZ3JhZG9zIGhhc3RhIDUxMjYgYmljaWNsZXRhcyBjb24gMTYgZ3JhZG9zKS4gTm8gb2JzdGFudGUsIGVzdGEgdGVuZGVuY2lhIGNyZWNpZW50ZSBzZSBmcmVuYSBlbiBsb3MgMTYgZ3JhZG9zLCBwYXJhIG1hbnRlbmVyc2UgZXN0YWJsZSBoYXN0YSBsb3MgMjYgZ3JhZG9zIGFwcm94aW1hZGFtZW50ZS4gQSBwYXJ0aXIgZGUgZXNhIHRlbXBlcmF0dXJhLCBwb3IgY2FkYSBncmFkbyBxdWUgYXVtZW50YSBsYSB0ZW1wZXJhdHVyYSBzZSB2ZW5kZXLDoW4gbWVub3MgYmljaWNsZXRhcy4NCkVuIG90cmFzIHBhbGFicmFzLCBzZSB2ZW5kZXLDoW4gbcOhcyBiaWNpY2xldGFzIGN1YW5kbyBsYSB0ZW1wZXJhdHVyYSBzZWEgYWdyYWRhYmxlICgxNi0yNiBncmFkb3MpLiBDdWFuZG8gaGFjZSBmcsOtbyBvIGNhbG9yLCBzZSB2ZW5kZW4gY2FkYSB2ZXogbWVub3MgYmljaWNsZXRhcy4NCkZpbmFsbWVudGUsIGhheSBxdWUgdGVuZXIgY3VpZGFkbyBjb24gbGFzIGV4cGxpY2FjaW9uZXMgY3VhbmRvIGxhcyB0ZW1wZXJhdHVyYXMgc29uIG1lbm9yZXMgcXVlIDAuNSBncmFkb3MgbyBtYXlvcmVzIHF1ZSAyOSwgcHVlcyBzZSB0aWVuZW4gbWVub3Mgb2JzZXJ2YWNpb25lcyBjdWFuZG8gbGEgdGVtcGVyYXR1cmEgdG9tYSBlc29zIHZhbG9yZXMuDQoNCi0gYEh1bWlkaXR5YDogTWllbnRyYXMgbGEgaHVtZWRhZCBlcyBtZW5vciBxdWUgNTAgZWwgbsO6bWVybyBkZSBiaWNpY2xldGFzIHZlbmRpZGFzIHNlIG1hbnRlbmRyw6Egc2llbXByZSBjb25zdGFudGUgZW4gNDcwMCBiaWNpY2xldGFzIGFwcm94aW1hZGFtZW50ZS4gVW5hIHZleiBzdXBlcmFkbyBlc2UgdW1icmFsIGRlbCA1MCUgZGUgaHVtZWRhZCwgc2Vnw7puIGF1bWVudGEgbGEgaHVtZWRhZCwgc2UgdmVuZGVuIGNhZGEgdmV6IG1lbm9zIGJpY2ljbGV0YXMsIGhhc3RhIGxsZWdhciBhIGxhcyAzNDkwIGJpY2ljbGV0YXMgdmVuZGlkYXMgY3VhbmRvIGxhIGh1bWVkYWQgZXMgZGVsIDk3JS4NCk5vIG9ic3RhbnRlLCBlc3RhcyBleHBsaWNhY2lvbmVzIGRlYmVuIHNlciByZXZpc2FkYXMgY3VhbmRvIGxhIGh1bWVkYWQgc2UgZW5jdWVudHJhIHBvciBkZWJham8gZGVsIDM3JSBvIHBvciBlbmNpbWEgZGVsIDkyJSwgcHVlcyBlbiBlc29zIGNhc29zIG5vIHNlIHRpZW5lbiBtdWNoYXMgb2JzZXJ2YWNpb25lcywgcG9yIGxvIHF1ZSBsYXMgZXhwbGljYWNpb25lcyBlbiBlc29zIGNhc29zIG5vIHNvbiBkZWwgdG9kbyBmaWFibGVzLg0KDQotIGBXaW5kIHNwZWVkYDogRWwgbW9kZWxvIHByZWRpY2UgcXVlIGxhIHRlbmRlbmNpYSBkZSBlc3RhIHZhcmlhYmxlIGVzIGNsYXJhbWVudGUgZGVjcmVjaWVudGUuIFNlZ8O6biBhdW1lbnRhIGVsIHZpZW50byBjYWRhIHZleiBzZSB2ZW5kZW4gbWVub3MgYmljaWNsZXRhcywgcGFzYW5kbyBkZSA0NjQwIGN1YW5kbyBlbCB2aWVudG8gdG9tYSB1biB2YWxvciBkZSAxLjUsIGEgMzk5MiBjdWFuZG8gZWwgdmllbnRvIHRvbWEgdW4gdmFsb3IgZGUgMjQuIFBhcmEgdmFsb3JlcyBtYXlvcmVzIHF1ZSAyNCwgZWwgbsO6bWVybyBkZSBiaWNpY2xldGFzIHZlbmRpZGFzIHNlIG1hbnRlbmRyw6EgZXN0YWJsZSBlbiBlc2UgdmFsb3IuDQpObyBvYnN0YW50ZSwgcGFyYSB2YWxvcmVzIG1heW9yZXMgcXVlIDI0LCBoYXkgcXVlIHJlY29uc2lkZXJhciBsYSBmaWFiaWxpZGFkIGRlIGVzdGFzIGV4cGxpY2FjaW9uZXMsIHB1ZXMgc2UgdGllbmVuIG11eSBwb2NhcyAobyBuaW5ndW5hKSBvYnNlcnZhY2lvbmVzIHBhcmEgZXNvcyB2YWxvcmVzIGVuIGVzdGEgdmFyaWFibGUuDQoNCiMjIEVYRVJDSVNFOg0KDQpHZW5lcmF0ZSBhIDJEIFBhcnRpYWwgRGVwZW5kZW5jeSBQbG90IHdpdGggaHVtaWRpdHkgYW5kIHRlbXBlcmF0dXJlIHRvIHByZWRpY3QgdGhlIG51bWJlciBvZiBiaWtlcyByZW50ZWQgZGVwZW5kaW5nIG9mIHRob3NlIHBhcmFtZXRlcnMuDQoNCkJFIENBUkVGVUw6IGR1ZSB0byB0aGUgc2l6ZSwgZXh0cmFjdCBhIHNldCBvZiByYW5kb20gc2FtcGxlcyBmcm9tIHRoZSBCQkREIGJlZm9yZSBnZW5lcmF0aW5nIHRoZSB0aGUgZGF0YSBmb3IgdGhlIFBhcnRpYWwgRGVwZW5kZW5jeSBQbG90LiANCg0KU2hvdyB0aGUgZGVuc2l0eSBkaXN0cmlidXRpb24gb2YgYm90aCBpbnB1dCBmZWF0dXJlcyB3aXRoIHRoZSAyRCBwbG90IGFzIHNob3duIGluIHRoZSBjbGFzcyBzbGlkZXMuIA0KDQpUSVA6IFVzZSBnZW9tX3RpbGUoKSB0byBnZW5lcmF0ZSB0aGUgMkQgcGxvdC4gU2V0IHdpZHRoIGFuZCBoZWlnaHQgdG8gYXZvaWQgaG9sZXMuIA0KDQojIyBRVUVTVElPTjoNCg0KSW50ZXJwcmV0IHRoZSByZXN1bHRzLg0KDQoNCmBgYHtyfQ0KDQpzYW1wbGVkIDwtIHNhbXBsZV9uKGRheXNfc2luY2UsIDQwKQ0KdGVtcCA8LSBzYW1wbGVkJHRlbXANCmh1bSA8LSBzYW1wbGVkJGh1bQ0KdGggPC0gaW5uZXJfam9pbihkYXRhLmZyYW1lKHRlbXApLGRhdGEuZnJhbWUoaHVtKSwgYnk9Y2hhcmFjdGVyKCkpDQp0aCRwIDwtIDANCg0KZm9yKGkgaW4gMTpucm93KHRoKSl7DQogIHIgPC0gZGF5c19zaW5jZQ0KICByW1sidGVtcCJdXSA8LSB0aFtbInRlbXAiXV1baV0NCiAgcltbImh1bSJdXSA8LSB0aFtbImh1bSJdXVtpXQ0KICANCiAgc2FsIDwtIHByZWRpY3QocmYsIHIpJHByZWRpY3RlZA0KICB0aFtbInAiXV1baV0gPC0gc3VtKHNhbCkgLyBucg0KfQ0KDQpnZ3Bsb3QodGgsIGFlcyh4PXRlbXAsIHk9aHVtKSkgKyBnZW9tX3RpbGUoYWVzKGZpbGw9cCwgd2lkdGg9MTAsIGhlaWdodD0xNSkpICsgZ2VvbV9ydWcoYWxwaGE9MC4wMSkNCmBgYA0KDQoqKkludGVycHJldGFjaW9uKioNCg0KUG9kZW1vcyBhcHJlY2lhciB1bmEgY2llcnRhIGluZGVwZW5kZW5jaWEgZW50cmUgZWwgZWZlY3RvIGRlIGxhIHRlbXBlcmF0dXJhIHkgZWwgZGUgbGEgaHVtZWRhZCwgeWEgcXVlLCBjb21vIHNlIGhhIHBvZGlkbyBvYnNlcnZhciBlbiBlbCBQRFAgZW4gMSBkaW1lbnNpw7NuIGRlIGFtYmFzIHZhcmlhYmxlcywgbWllbnRyYXMgbGEgaHVtZWRhZCBzZSBtYW50ZW5nYSBwb3IgZGViYWpvIGRlbCA1MCUgZWwgbsO6bWVybyBkZSBiaWNpY2xldGFzIHZlbmRpZGFzIHNlcsOhIG3DoXhpbW8sIHkgYSBwYXJ0aXIgZGUgZXNlIHVtYnJhbCBpcsOhIGRpc21pbnV5ZW5kbyBlbCBuw7ptZXJvIGRlIHZlbnRhcyBzZWfDum4gYXVtZW50ZSBsYSBodW1lZGFkLiBQb3Igb3RybyBsYWRvLCBlbiBlbCBjYXNvIGRlIGxhIHRlbXBlcmF0dXJhLCBwYXJ0aWVuZG8gZGVzZGUgbGEgdGVtcGVyYXR1cmEgbcOtbmltYSwgc2Vnw7puIGF1bWVudGEgbGEgdGVtcGVyYXR1cmEgYXVtZW50YSBlbCBuw7ptZXJvIGRlIHZlbnRhcywgaGFzdGEgbGxlZ2FyIGEgdW4gbcOheGltbyBzb2JyZSBsb3MgMTYgZ3JhZG9zLCBkb25kZSBlbCBuw7ptZXJvIGRlIGJpY2ljbGV0YXMgdmVuZGlkYXMgc2UgbWFudGllbmUgY29uc3RhbnRlIGhhc3RhIGxsZWdhciBhIGxvcyAyNiBncmFkb3MuIEEgcGFydGlyIGRlIGFow60gdnVlbHZlIGEgZGlzbWludWlyIGVsIG7Dum1lcm8gZGUgYmljaWNsZXRhcyB2ZW5kaWRhcyBzZWfDum4gYXVtZW50YSBsYSB0ZW1wZXJhdHVyYS4NCg0KRW4gZWwgUERQIGVuIDJEIHBvZGVtb3MgYXByZWNpYXIgZXN0b3MgbWlzbW9zIGZlbsOzbWVub3MgZGUgbWFuZXJhIGlkw6ludGljYS4gVmVtb3MgcXVlIGVsIG7Dum1lcm8gbcOheGltbyBkZSB2ZW50YXMgc2UgYWxjYW56YXLDoSBjdWFuZG8gbGEgdGVtcGVyYXR1cmEgc2VhIGFncmFkYWJsZSAoZW4gdG9ybm8gYSAyMCBncmFkb3MpIHkgbGEgaHVtZWRhZCBzZWEgbWVub3IgcXVlIGVsIDUwJTsgbWllbnRyYXMgcXVlIGVzdGUgbsO6bWVybyBhbGNhbnphcsOhIHN1IG3DrW5pbW8gY3VhbmRvIGxhIHRlbXBlcmF0dXJhIHNlYSBleHRyZW1hZGFtZW50ZSBiYWphIHkgbGEgaHVtZWRhZCBtdXkgYWx0YS4NCg0KQXN1bWltb3MgcXVlIGxvcyBlZmVjdG9zIGRlIGFtYmFzIHZhcmlhYmxlcyBzb24gaW5kZXBlbmRpZW50ZXMgcG9ycXVlIGVzdG9zIG1pc21vcyBlZmVjdG9zIHNlIHB1ZWRlbiBhcHJlY2lhciBwb3Igc2VwYXJhZG8sIHNpbiBoYWNlciB1c28gZGVsIFBEUCBlbiAyIGRpbWVuc2lvbmVzLCB5IHBvciB0YW50byBubyBlcyBkZSBpbnRlcsOpcyBlc3R1ZGlhciBzdSBpbnRlcmFjY2nDs24uDQoNClRhbWJpw6luIGhheSBxdWUgdGVuZXIgZW4gY29uc2lkZXJhY2nDs24gcXVlIG5vIHNlIHRpZW5lbiBtdWNoYXMgb2JzZXJ2YWNpb25lcyByZWFsZXMgZGUgdG9kb3MgbG9zIHZhbG9yZXMgcXVlIHNlIGhhbiBwcm9iYWRvLCBjb24gbG8gY3VhbCBlbCBtb2RlbG8gbm8gc2UgaGEgcG9kaWRvIGVudHJlbmFyIGVuIGVzb3MgZXNjZW5hcmlvcyB5IGxhcyBleHBsaWNhY2lvbmVzIHB1ZWRlbiBubyBzZXIgZmlhYmxlcy4gRXMgZWwgY2FzbyBkZSBsYSBodW1lZGFkIGN1YW5kbyB0b21hIHZhbG9yZXMgcG9yIGRlYmFqbyBkZWwgMzclIG8gcG9yIGVuY2ltYSBkZWwgOTIlOyBvIGRlIGxhIHRlbXBlcmF0dXJhIGN1YW5kbyBlc3TDoSBwb3IgZGViYWpvIGRlIDAuNSBvIHBvciBlbmNpbWEgZGUgMjkgZ3JhZG9zLg0KQWRlbcOhcywgY2FiZSBtZW5jaW9uYXIgcXVlIG5vIHNlIHRpZW5lbiBkYXRvcyB0YW1wb2NvIGRlIGN1w6FudGFzIG9ic2VydmFjaW9uZXMgZGVsIGNvbmp1bnRvIHNlIHRpZW5lbiBwYXJhIGNhZGEgcG9zaWJsZSBjYXNvIGRlIGludGVyYWNjacOzbiBkZSBhbWJhcyB2YXJpYWJsZXMuDQoNCiMjIEVYRVJDSVNFOg0KDQpBcHBseSB0aGUgcHJldmlvdXMgY29uY2VwdHMgdG8gcHJlZGljdCB0aGUgKipwcmljZSoqIG9mIGEgaG91c2UgZnJvbSB0aGUgZGF0YWJhc2UgKiprY19ob3VzZV9kYXRhLmNzdioqLiBJbiB0aGlzIGNhc2UsIHVzZSBhZ2FpbiBhIHJhbmRvbSBmb3Jlc3QgYXBwcm94aW1hdGlvbiBmb3IgdGhlIHByZWRpY3Rpb24gYmFzZWQgb24gdGhlIGZlYXR1cmVzICoqYmVkcm9vbXMqKiwgKipiYXRocm9vbXMqKiwgKipzcWZ0X2xpdmluZyoqLCAqKnNxZnRfbG90KiosICoqZmxvb3JzKiogYW5kICoqeXJfYnVpbHQqKi4gDQpVc2UgdGhlIHBhcnRpYWwgZGVwZW5kZW5jZSBwbG90IHRvIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwcyB0aGUgbW9kZWwgbGVhcm5lZC4NCg0KQkUgQ0FSRUZVTDogZHVlIHRvIHRoZSBzaXplLCBleHRyYWN0IGEgc2V0IG9mIHJhbmRvbSBzYW1wbGVzIGZyb20gdGhlIEJCREQgYmVmb3JlIGdlbmVyYXRpbmcgdGhlIGRhdGEgZm9yIHRoZSBQYXJ0aWFsIERlcGVuZGVuY3kgUGxvdC4gDQoNCiMjIFFVRVNUSU9OOg0KDQpBbmFseXNlIHRoZSBpbmZsdWVuY2Ugb2YgKipiZWRyb29tcywgYmF0aHJvb21zLCBzcWZ0X2xpdmluZyoqIGFuZCAqKmZsb29ycyoqIG9uIHRoZSBwcmVkaWN0ZWQgcHJpY2UuDQoNCg0KYGBge3J9DQoNCmQgPC0gcmVhZC5jc3YoImtjX2hvdXNlX2RhdGEuY3N2IikNCg0Kc2V0LnNlZWQoNTApDQoNCnNhbXBsZWQgPC0gc2FtcGxlX24oZCwgMTAwMCkNCg0Kc2FtcGxlZCA8LSBzZWxlY3Qoc2FtcGxlZCwgYmVkcm9vbXMsIGJhdGhyb29tcywgc3FmdF9saXZpbmcsIHNxZnRfbG90LCBmbG9vcnMsIHlyX2J1aWx0LCBwcmljZSkNCg0KcmYgPC0gcmZzcmMocHJpY2V+LiwgZGF0YT1zYW1wbGVkKQ0KDQpyZXN1bHRzIDwtIHNlbGVjdChzYW1wbGVkLCBiZWRyb29tcywgYmF0aHJvb21zLCBzcWZ0X2xpdmluZywgZmxvb3JzLCBwcmljZSkNCm5yIDwtIG5yb3coc2FtcGxlZCkNCmZvcihjIGluIG5hbWVzKHJlc3VsdHMpWzE6NF0pDQp7DQogIGZvcihpIGluIDE6bnIpew0KICAgIHIgPC0gc2FtcGxlZA0KICAgIHJbW2NdXSA8LSBzYW1wbGVkW1tjXV1baV0NCiAgICBzYWwgPC0gcHJlZGljdChyZiwgcikkcHJlZGljdGVkDQogICAgcmVzdWx0c1tbY11dW2ldIDwtIHN1bShzYWwpIC8gbnINCiAgfQ0KfQ0KDQpnMSA8LSBnZ3Bsb3Qoc2FtcGxlZCwgYWVzKHg9YmVkcm9vbXMsIHk9cmVzdWx0cyRiZWRyb29tcykpICsgZ2VvbV9saW5lKCkgKyBnZW9tX3J1ZyhhbHBoYT0wLjEsIHNpZGVzPSJiIikgKyB5bGFiKCJQcmVkaWN0aW9uIikgKyB4bGFiKCJCZWRyb29tcyIpDQoNCmcyIDwtIGdncGxvdChzYW1wbGVkLCBhZXMoeD1iYXRocm9vbXMsIHk9cmVzdWx0cyRiYXRocm9vbXMpKSArIGdlb21fbGluZSgpICsgZ2VvbV9ydWcoYWxwaGE9MC4xLCBzaWRlcz0iYiIpICsgeGxhYigiQmF0aHJvb21zIikNCg0KZzMgPC0gZ2dwbG90KHNhbXBsZWQsIGFlcyh4PXNxZnRfbGl2aW5nLCB5PXJlc3VsdHMkc3FmdF9saXZpbmcpKSArIGdlb21fbGluZSgpICsgZ2VvbV9ydWcoYWxwaGE9MC4xLCBzaWRlcz0iYiIpICsgeGxhYigiU3FmdCBMaXZpbmciKQ0KDQpnNCA8LSBnZ3Bsb3Qoc2FtcGxlZCwgYWVzKHg9Zmxvb3JzLCB5PXJlc3VsdHMkZmxvb3JzKSkgKyBnZW9tX2xpbmUoKSArIGdlb21fcnVnKGFscGhhPTAuMSwgc2lkZXM9ImIiKSsgeGxhYigiRmxvb3JzIikNCg0Kc3VicGxvdChnMSwgZzIsIGczLCBnNCwgdGl0bGVYID0gVFJVRSwgc2hhcmVYID0gRkFMU0UpDQpgYGANCg0KKipJbnRlcnByZXRhY2lvbioqDQoNCi0gYGJlZHJvb21zYDogRXN0YSB2YXJpYWJsZSBzZSBjb21wb3J0YSBkZSB1bmEgbWFuZXJhIGN1cmlvc2EsIHB1ZXMsIHBhcnRpZW5kbyBkZSAwIGhhYml0YWNpb25lcywgYSBtw6FzIGhhYml0YWNpb25lcywgbcOhcyBiYXJhdGEgc2Vyw6EgbGEgdml2aWVuZGEsIGhhc3RhIGxsZWdhciBhIDQgaGFiaXRhY2lvbmVzLiBBIHBhcnRpciBkZSBlc2UgcHVudG8gc2UgaW52aWVydGUgbGEgdGVuZGVuY2lhLCB5IGEgbWF5b3IgbsO6bWVybyBkZSBoYWJpdGFjaW9uZXMsIG1heW9yIHByZWNpby4gTm8gb2JzdGFudGUsIG5vIHNlIHB1ZWRlIGFmaXJtYXIgY29uIGZpcm1lemEgZXN0bywgeWEgcXVlIHNlIHRpZW5lbiBwb2NhcyBvYnNlcnZhY2lvbmVzIHBhcmEgbGEgdmFyaWFibGUgY3VhbmRvIHNlIHRpZW5lbiAwIG8gbcOhcyBkZSA2IGhhYml0YWNpb25lcy4NCg0KLSBgYmF0aHJvb21zYDogU2UgcHVlZGUgYXByZWNpYXIgY2xhcmFtZW50ZSBjb21vIGEgbWF5b3IgbsO6bWVybyBkZSBjdWFydG9zIGRlIGJhw7FvcywgbWF5b3Igc2Vyw6EgZWwgcHJlY2lvIGRlIGxhIHZpdmllbmRhIGVuIGN1ZXN0acOzbi4gTm8gb2JzdGFudGUsIGVzdGEgYWZpcm1hY2nDs24gbm8gZXMgZGVsIHRvZG8gZmlhYmxlIHBhcmEgMCBvIG3DoXMgZGUgNCBiYcOxb3MsIHB1ZXMgYXBlbmFzIHNlIHRpZW5lbiBtdWVzdHJhcyBkZSBlbnRyZW5hbWllbnRvIGNvbiBlc3RvcyB2YWxvcmVzLg0KDQotIGBzcWZ0X2xpdmluZ2A6IFBvZGVtb3MgYXByZWNpYXIgY29tbyBjbGFyYW1lbnRlLCBjdcOhbnRvIG1heW9yIHNlYSBsYSBzdXBlcmZpY2llIGRlbCBzYWzDs24sIG3DoXMgY2FyYSBzZXLDoSBsYSB2aXZpZW5kYSBlbiBjdWVzdGnDs24uIE5vIG9ic3RhbnRlLCBlc3RhIGFmaXJtYWNpw7NuIGVzdMOhIGVuIGR1ZGEgcGFyYSB2YWxvcmVzIG1lbm9yZXMgcXVlIDU2MCBwaWVzIGN1YWRyYWRvcyB5IG1heW9yZXMgcXVlIDQ5MDAgcGllcyBjdWFkcmFkb3MuDQoNCi0gYGZsb29yc2A6IEVuIGVzdGEgdmFyaWFibGUgcG9kZW1vcyBhZmlybWFyIHF1ZSBhIG1heW9yIG7Dum1lcm8gZGUgcGlzb3MsIG1heW9yIHByZWNpbyB0ZW5kcsOhIGxhIHZpdmllbmRhLCB5IGVzIHVuYSBleHBsaWNhY2nDs24gZmlhYmxlLCBwdWVzIHNlIHRpZW5lbiBvYnNlcnZhY2lvbmVzIHBhcmEgdG9kb3MgbG9zIHZhbG9yZXMgZW4gZWwgc2V0IGRlIGVudHJlbmFtaWVudG8u